package cc.codeshop.scroller;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.widget.FrameLayout;
import android.widget.OverScroller;

public class ScrollView extends FrameLayout {

	private String tag = "ScrollView";
//	private Scroller mScroller;
	private OverScroller mScroller;
	private VelocityTracker mVelocityTracker;
	private int mLastMotionY;
	private int mOverscrollDistance;
	private int mMaximumVelocity;
	private int mMinimumVelocity;

	public ScrollView(Context context, AttributeSet attrs) {
		super(context, attrs);
		initScrollView();
	}
	
	private void initScrollView() {
//		mScroller = new Scroller(getContext());
		mScroller = new OverScroller(getContext());
		final ViewConfiguration configuration = ViewConfiguration.get(getContext());
		mOverscrollDistance = configuration.getScaledOverscrollDistance();
//		mOverscrollDistance = 100;
		mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
		mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
		Log.v(tag, "mOverscrollDistance = "  + mOverscrollDistance + ", mMaximumVelocity = " + mMaximumVelocity + ", mMinimumVelocity = " + mMinimumVelocity);		//结果：0
	}
	
	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
		
        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        if (heightMode == MeasureSpec.UNSPECIFIED) {
        	Log.e(tag, "UNSPECIFIED");
            return;
        }
		
		if(getChildCount() > 0) {
			final View child = getChildAt(0);
			int height = getMeasuredHeight();
			int childHeight = child.getMeasuredHeight();
			Log.d(tag, "height = " + height + ", childHeight = " + childHeight);
			//结果：height = 407, childHeight = 407，因super.onMeasure(..)实现中，此View截取掉了子View多出来的高度，
			if(childHeight <= height) {
				final FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams)child.getLayoutParams();
				int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, getPaddingLeft() + getPaddingRight(), lp.width);
				height -= getPaddingTop();
				height -= getPaddingBottom();
				int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
				child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
			}
		}
	}
	
	//ViewGroup.onMeasure(..)中调用此方法，默认实现就调用getChildMeasureSpec(..),  子View的高度取此View的高度，这里改写它，让子View取它自己实际的高度
	@Override
	protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) {	
		final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
		final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, getPaddingLeft() + getPaddingRight() + lp.leftMargin + lp.rightMargin + widthUsed, lp.width);
		final int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(lp.topMargin + lp.bottomMargin, MeasureSpec.UNSPECIFIED);		//UNSPECIFIED的话，子View就会取它自己实际的高度
		child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
	}
	
	@Override
	protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
		super.onLayout(changed, left, top, right, bottom);
		Log.v(tag, "onLayout(" + changed + ", " + left + ", " + top + ", " + right + ", " + bottom + ")");
	}

	@Override
	public boolean onInterceptTouchEvent(MotionEvent ev) {
		boolean consume = super.onInterceptTouchEvent(ev);
		Log.v(tag, "onInterceptTouchEvent(" + ev.getX() + ", " + ev.getY() + ", " + ev.getAction() + "), " + consume);
		return consume;
	}

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		boolean consume = super.onTouchEvent(event);
		consume = true;
		Log.v(tag, "onTouchEvent(" + event.getX() + ", " + event.getY() + ", " + event.getAction() + "), " + consume);
		
		initVelocityTrackerIfNotExists();
		mVelocityTracker.addMovement(event);

		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			if( ! mScroller.isFinished()) {
				mScroller.abortAnimation();
				Log.v(tag, "Down !mScroller.isFinished() :  true");
			}
			mLastMotionY = (int)event.getY();
			break;
		case MotionEvent.ACTION_MOVE:
			 int y = (int)event.getY();
			int deltaY = mLastMotionY - y;
//			scrollBy(0, deltaY);
			overScrollBy(0, deltaY, 0, getScrollY(), 0, getScrollRange(), 0, mOverscrollDistance, true);
			mLastMotionY = y;
			break;

		case MotionEvent.ACTION_UP:
			final VelocityTracker velocityTracker = mVelocityTracker;
			velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
			int initialVelocity = (int) velocityTracker.getYVelocity();
			if(getChildCount() > 0) {
				if(Math.abs(initialVelocity) > mMinimumVelocity) {
					fling(-initialVelocity);
				} else {
					if(mScroller.springBack(0, getScrollY(), 0, 0, 0, getScrollRange())){		//回滚到一个有效的位置
						postInvalidateOnAnimation();
					}
				}
			}
			recyleVelocityTracker();
			break;
		}
//		postInvalidate();
		return consume;
	}
	
	/**
	 * Fling操作
	 * @param velocityY The initial velocity in the Y direction. Positive  numbers mean that the finger/cursor is moving down the screen,  which means we want to scroll towards the top.
	 */
	public void fling(int velocityY) {
		int bottom = getChildAt(0).getBottom();
		//fling(int startX, int startY, int velocityX, int velocityY, int minX, int maxX, int minY, int maxY, int overX, int overY)
//		mScroller.fling(0, getScrollY(), 0, velocityY, 0, 0, 0, Math.max(0, bottom - getHeight()), 0, getHeight() / 2);
		mScroller.fling(0, getScrollY(), 0, velocityY, 0, 0, 0, Math.max(0, bottom - getHeight()), 0, getHeight() / 2);
	}
	
	@Override
	protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) {  //overScrollBy(..)并没有作实际的滚动，而是触发onOverScrolled(..)
		if(!mScroller.isFinished()) {		//computeScroll()中过来，针对fling操作
			setScrollY(scrollY);			//Fling操作时，computeScroll()中，只会进入if(mScroller.computeScrollOffset()) {}一次。而这句，使得多次进入该if，可以理解为不断设scrollY值动画就算还没有停止
			if(clampedY) {
				//Call this when you want to 'spring back' into a valid coordinate range. true if a springback was initiated, false if startX and startY were already within the valid range.
				Log.w(tag, "onOverScrolled : " + getScrollY());
				mScroller.springBack(0, getScrollY(),  0, 0, 0, getScrollRange());
			}
		} else {			//Move操作			
			super.scrollTo(scrollX, scrollY);			//在这里作实际的滚动，
		}
	}
	
	@Override
	public void computeScroll() {
		if(mScroller.computeScrollOffset()) {
			Log.v(tag, "computeScroll() true");
			int oldY = getScrollY();
			int y = mScroller.getCurrY();
			if(oldY != y) {				
				overScrollBy(0, y - oldY, 0, oldY, 0, getScrollRange(), 0, mOverscrollDistance, false);
			} else {
				Log.w(tag, "==");
			} 
//			postInvalidateOnAnimation();
		} else {			
			Log.v(tag, "computeScroll() false");
		}
	}
	
	/**
	 * 获取可滚动的距离（子View高度减去此View高度，即子View比此View多出的部分）
	 * @return
	 */
	private int getScrollRange() {
		int scrollRange = 0;
		if(getChildCount() > 0) {
			View child = getChildAt(0);
//			Log.d(tag, "getScrollRange : height = " + getHeight() + ", childHeight = " + child.getHeight() + ", childMeasureHeight = " + child.getMeasuredHeight());
			scrollRange = Math.max(0, child.getHeight() - (getHeight() - getPaddingTop() - getPaddingBottom()));
		}
//		Log.v(tag, "scrollRange = " + scrollRange);
		return scrollRange;
	}
	
	private void initVelocityTrackerIfNotExists() {
		if(mVelocityTracker == null) {
			mVelocityTracker = VelocityTracker.obtain();
		}
	}
	
	private void recyleVelocityTracker() {
		if(mVelocityTracker != null) {
			mVelocityTracker.recycle();
			mVelocityTracker = null;
		}
	}

}
